﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using MediaAssistant.Constants;
using MediaAssistant.Controls.AlternativeLocation;
using MediaAssistant.Controls.StatusMessageBar;
using MediaAssistant.DAL.Virtualization;
using MediaAssistant.EventArguments;
using MediaAssistant.Helper;
using MediaAssistant.Management;
using MediaAssistant.MusicDJ;
using MefBasic.Commans;
using MefBasic.Data;
using MefBasic.Extensions;
using MefBasic.Helper;
using MefBasic.Threading;
using Microsoft.Practices.Composite;
using Microsoft.Practices.Composite.Events;
using Microsoft.Win32;
using MediaAssistant.DAL;
using MediaAssistant.DAL.Constants;
using MediaAssistant.DAL.Helper;
using Newtonsoft.Json.Linq;

namespace MediaAssistant.Data
{
    [Serializable]
    [Export(typeof(LibraryDataSource))]
    public class LibraryDataSource : DependencyNotifyObject
    {
        public static readonly DependencyProperty SelectedLibraryItemProperty =
            DependencyProperty.Register("SelectedLibraryItem", typeof(LibraryItem), typeof(LibraryDataSource),
                                        new UIPropertyMetadata(null, OnPropertyChanged));

        [ImportingConstructor]
        public LibraryDataSource(IEventAggregator eventAggregator)
        {
            EventAggregator = eventAggregator;
            ResultMusics = new ObservableCollection<Music>();
            ResultMovies = new ObservableCollection<Movie>();
            MusicPlayer = new MediaElement { LoadedBehavior = MediaState.Manual };
            PlayerUpdateTimer = new Timer(OnPlayerUpdateTimer, null, 0, 1000);
            Playlists = new ObservableCollection<LibraryItem>();

            PlayMovieCommand = new DelegateCommand(ExecutePlayMovie, CanExecutePlayMovie);
            ShowMovieInWindowsExplorerCommand = new DelegateCommand(ExecuteShowMovieInWindowsExplorer, CanExecuteShowMovieInWindowsExplorer);
            ShowMovieInIMDBCommand = new DelegateCommand(ExecuteShowMovieInIMDB, CanExecuteShowMovieInIMDB);
            SaveMovieStateCommand = new DelegateCommand(ExecuteSaveMovieState, CanExecuteSaveMovieState);
            ShowAlternativeLocationsCommand = new DelegateCommand(ExecuteShowAlternativeLocations);
            MarkeAllFailedAsPendingCommand = new DelegateCommand(ExecuteMarkeAllFailedAsPending);

            MarkeAsPendingCommand = new DelegateCommand(ExecuteMarkeAsPending, CanExecuteExecuteMarkeAsPending);
            SelectLibraryItemCommand = new DelegateCommand(ExecuteSelectLibraryItem);

            RootItems = new ObservableCollection<LibraryItem>();

        }

        public DelegateCommand MarkeAllFailedAsPendingCommand { get; set; }

        private void ExecuteMarkeAllFailedAsPending(object obj)
        {
            foreach (var movie in ResultMovies.Where(m=>m.Status==MovieStatus.Failed))
            {
                DatabaseManager.Instance.MarkAsPending(movie);    
            }
            DatabaseManager.Instance.UpdateProcessingMovies();
            DatabaseManager.Instance.UpdateProcessingTitle(ShowDrivesMoviesOnly);
        }

        [Import]
        private Lazy<LibraryManager> LibraryManager { get; set; }

        public DelegateCommand SelectLibraryItemCommand { get; set; }

        private void ExecuteSelectLibraryItem(object obj)
        {
            SelectedLibraryItem = (LibraryItem)obj;
        }


        private bool CanExecuteExecuteMarkeAsPending(object arg)
        {
            return SelectedMovie != null;
        }

        public DelegateCommand MarkeAsPendingCommand { get; set; }

        private void ExecuteMarkeAsPending(object obj)
        {
            DatabaseManager.Instance.MarkAsPending(SelectedMovie);
            DatabaseManager.Instance.UpdateProcessingMovies();
            DatabaseManager.Instance.UpdateProcessingTitle(ShowDrivesMoviesOnly);
        }

        public DelegateCommand ShowAlternativeLocationsCommand { get; set; }

        private void ExecuteShowAlternativeLocations(object obj)
        {
            Resolve<AlternativeLocationPresenter>().ShowAlternativeLocations(SelectedMovie);
        }


        private bool CanExecuteSaveMovieState(object arg)
        {
            return SelectedMovie != null;
        }

        private void ExecuteSaveMovieState(object obj)
        {
            lock (DatabaseManager.LockObject)
            {
                var profileMovie = DatabaseManager.Instance.GetOrCreateProfileMovie(SelectedProfile, SelectedMovie);
                if (IsFavoriteMovieChanged(profileMovie))
                {
                    WaitScreen.Show();
                    StatusMessageService.SetStatusMessage("Updating recommended movies...");
                    TasteKidService.Value.UpdateRecommendation(SelectedMovie);
                    StatusMessageService.SetStatusMessage(StatusMessages.Ready);
                    WaitScreen.Hide();
                }
                DatabaseManager.Instance.UpdateProfile(profileMovie, SelectedMovie.IsStarred ? 1 : 0, SelectedMovie.Watched, SelectedMovie.IsInWishList, SelectedMovie.IsNew);
                UpdateTitles();
            }
        }

        public void UpdateTitles()
        {
            DatabaseManager.Instance.UpdateTitles(ShowDrivesMoviesOnly, SelectedProfile);
        }


        private bool _showDrivesMoviesOnly;
        public bool ShowDrivesMoviesOnly
        {
            get { return _showDrivesMoviesOnly; }
            set
            {
                _showDrivesMoviesOnly = value;
                if (ResultMusics.Count > 0 || ResultMovies.Count > 0)
                    SynchronizationContextHelper.Instance.SendInBackground(d => RefreshResult(), null);
                if (SelectedProfile != null)
                    DatabaseManager.Instance.UpdateTitles(ShowDrivesMoviesOnly, SelectedProfile);
                OnPropertyChanged("ShowDrivesMoviesOnly");
            }
        }

        [Import]
        private Lazy<TasteKidService> TasteKidService { get; set; }


        private bool IsFavoriteMovieChanged(ProfileMovie profileMovie)
        {
            return profileMovie.Star > 0 != SelectedMovie.IsStarred || profileMovie.IsInWishList != SelectedMovie.IsInWishList;
        }

        public DelegateCommand SaveMovieStateCommand { get; set; }

        private bool CanExecuteShowMovieInIMDB(object arg)
        {
            return SelectedMovie != null && SelectedMovie.IMDBId != null;
        }

        private bool CanExecuteShowMovieInWindowsExplorer(object arg)
        {
            return SelectedMovie != null;
        }

        private bool CanExecutePlayMovie(object arg)
        {
            return SelectedMovie != null;
        }

        private void RaiseCanExecuteChanged()
        {
            PlayMovieCommand.RaiseCanExecuteChanged();
            ShowMovieInWindowsExplorerCommand.RaiseCanExecuteChanged();
            ShowMovieInIMDBCommand.RaiseCanExecuteChanged();
            SaveMovieStateCommand.RaiseCanExecuteChanged();
            ShowAlternativeLocationsCommand.RaiseCanExecuteChanged();
            MarkeAsPendingCommand.RaiseCanExecuteChanged();
            LibraryManager.Value.RaiseCanExecuteChanged();
        }

        public DelegateCommand ShowMovieInIMDBCommand { get; set; }

        private void ExecuteShowMovieInIMDB(object obj)
        {
            if (SelectedMovie.IMDBId != null)
            {
                var imdbUrl = String.Format("http://www.imdb.com/title/{0}/", SelectedMovie.IMDBId);
                SystemHelper.OpenUrl(imdbUrl);
            }
        }

        private void ExecutePlayMovie(object obj)
        {
            var selectedMovie = obj as Movie ?? SelectedMovie;
            if(selectedMovie==null)
                return;
            var fileToPlay = selectedMovie.GetFileToPlay();
            if (VerifyFileLocation(selectedMovie,ref fileToPlay))
                Play(selectedMovie,fileToPlay);
        }


        private int _recommendedMovieCount;
        public int RecommendedMovieCount
        {
            get { return _recommendedMovieCount; }
            set
            {
                _recommendedMovieCount = value;
                OnPropertyChanged("RecommendedMovieCount");
            }
        }


        public DelegateCommand ShowMovieInWindowsExplorerCommand { get; set; }

        private void ExecuteShowMovieInWindowsExplorer(object obj)
        {
            var selectedMovie = obj as Movie ?? SelectedMovie;
            if(selectedMovie==null)
                return;
            var fileToPlay = selectedMovie.GetFileToPlay();
            if (VerifyFileLocation(selectedMovie, ref fileToPlay))
                SystemHelper.LocateInWndowsExplorer(fileToPlay);
        }

        public DelegateCommand PlayMovieCommand { get; set; }


        private ObservableCollection<IMusicDJ> _musicDjs;

        [ImportMany(typeof(IMusicDJ))]
        public ObservableCollection<IMusicDJ> MusicDjs
        {
            get { return _musicDjs; }
            set
            {
                _musicDjs = value;
                OnPropertyChanged("MusicDjs");
            }
        }


        private IMusicDJ _selectedMusicDJ;
        public IMusicDJ SelectedMusicDJ
        {
            get { return _selectedMusicDJ; }
            set
            {
                _selectedMusicDJ = value;
                OnPropertyChanged("SelectedMusicDJ");
            }
        }

        private ObservableCollection<LibraryItem> _playlists;
        public ObservableCollection<LibraryItem> Playlists
        {
            get { return _playlists; }
            set
            {
                _playlists = value;
                OnPropertyChanged("Playlists");
            }
        }


        protected Timer PlayerUpdateTimer { get; set; }

        private void OnPlayerUpdateTimer(object state)
        {
            SynchronizationContextHelper.Instance.SendInBackground(UpdateTimer, state);
        }

        public Music PlayingMusic
        {
            get { return (Music)GetValue(PlayingMusicProperty); }
            set { SetValue(PlayingMusicProperty, value); }
        }

        public static readonly DependencyProperty PlayingMusicProperty =
            DependencyProperty.Register("PlayingMusic", typeof(Music), typeof(LibraryDataSource), new UIPropertyMetadata(null, OnPropertyChanged));


        public ObservableCollection<LibraryItem> RootItems
        {
            get { return (ObservableCollection<LibraryItem>)GetValue(RootItemsProperty); }
            set { SetValue(RootItemsProperty, value); }
        }

        public static readonly DependencyProperty RootItemsProperty =
            DependencyProperty.Register("RootItems", typeof(ObservableCollection<LibraryItem>), typeof(LibraryDataSource), new UIPropertyMetadata(null));


        private ObservableCollection<string> _currentArtists;
        public ObservableCollection<string> CurrentArtists
        {
            get { return _currentArtists; }
            set
            {
                _currentArtists = value;
                OnPropertyChanged("CurrentArtists");
            }
        }

        private ObservableCollection<Music> _resultMusics;
        public ObservableCollection<Music> ResultMusics
        {
            get { return _resultMusics; }
            set
            {
                _resultMusics = value;
                OnPropertyChanged("ResultMusics");
            }
        }

        private ObservableCollection<Movie> _resultMovies;
        public ObservableCollection<Movie> ResultMovies
        {
            get { return _resultMovies; }
            set
            {
                _resultMovies = value;
                OnPropertyChanged("ResultMovies");
            }
        }


        public LibraryItem SelectedLibraryItem
        {
            get { return (LibraryItem)GetValue(SelectedLibraryItemProperty); }
            set
            {
                if (value == SelectedLibraryItem)
                {
                    LoadSelectedItemResults();
                }
                SetValue(SelectedLibraryItemProperty, value);
            }
        }

        public IEventAggregator EventAggregator { get; private set; }

        public void LoadLibrary()
        {
            Logger.Instance.Write("Loading Library");
            Logger.Instance.Write("CreateFixedLibraryItems");
            DatabaseManager.Instance.CreateFixedLibraryItems();
            foreach (var rootItem in DatabaseManager.Instance.GetRootLibraryItems())
            {
                SynchronizationContextHelper.Instance.Send(d => RootItems.Add((LibraryItem)d), rootItem);
            }
            RefreshOrderedLibraryItems();

            foreach (var musicDj in MusicDjs)
            {
                musicDj.DataSource = this;
            }
            SelectedMusicDJ = MusicDjs.FirstOrDefault(m => m.Name == MusicDJName.SmartDJ);
            Playlists.Clear();
            Playlists.AddRange(DatabaseManager.Instance.GetAllPlaylists());
            ShowDrivesMoviesOnly = RegistryHelper.ShowDrivesMoviesOnly;
            Logger.Instance.Write("Loading Library Successfull");
        }

        public void RefreshOrderedLibraryItems()
        {
            var job = new Job(j =>
                                  {
                                      var rootItems = j.Store.GetObject<ObservableCollection<LibraryItem>>();
                                      var orderedLibraryItems = new VirtualizingCollection<LibraryItem>(new LibraryItemProvider(rootItems));
                                      j.Store.Add(orderedLibraryItems);
                                  });
            job.Store.Add(RootItems);
            job.JobCompleted+=(s,e)=> OrderedLibraryItems = job.Store.GetObject<VirtualizingCollection<LibraryItem>>();
            job.Start();
        }

        private IList<LibraryItem> _orderedLibraryItems;
        public IList<LibraryItem> OrderedLibraryItems
        {
            get { return _orderedLibraryItems; }
            set
            {
                _orderedLibraryItems = value;
                OnPropertyChanged("OrderedLibraryItems");
            }
        }

        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var dataSource = (LibraryDataSource)d;
            dataSource.DoPropertyChanged(e);
        }

        public bool CanPlay
        {
            get { return (bool)GetValue(CanPlayProperty); }
            set { SetValue(CanPlayProperty, value); }
        }

        public static readonly DependencyProperty CanPlayProperty =
            DependencyProperty.Register("CanPlay", typeof(bool), typeof(LibraryDataSource), new UIPropertyMetadata(false));


        public void DoPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            switch (e.Property.Name)
            {
                case "SelectedLibraryItem":
                    LoadSelectedItemResults();
                    break;
                case "PlayingMusic":
                    MusicPlayer.Source = PlayingMusic != null ? GetPlayingMusicUri(PlayingMusic.FullPath) : null;
                    if (e.OldValue != null)
                    {
                        DatabaseManager.Instance.SetMusicStatus((Music)e.OldValue, PlayingMusic);
                    }
                    if (e.NewValue != null)
                    {
                        DatabaseManager.Instance.SetMusicStatus((Music)e.NewValue, PlayingMusic);
                    }
                    CanPlay = SelectedMusic != null || PlayingMusic != null;
                    EventAggregator.GetEvent<BaseEvent<PlayingMusicEventArgs>>().Publish(new PlayingMusicEventArgs { Sender = this, Music = PlayingMusic });
                    break;
                case "IsPlaying":
                    if (PlayingMusic == null && SelectedMusic != null)
                    {
                        WaitScreen.Show();
                        Play(SelectedMusic);
                        var job = new Job(j =>
                                              {
                                                  var playingMusic = j.Store.GetObject<Music>();
                                                  var resultMusics = j.Store.GetObject<ObservableCollection<Music>>();
                                                  DatabaseManager.Instance.SetNowPlayingPlaylist(resultMusics, resultMusics, playingMusic);
                                                  StartMusicDJ(playingMusic);

                                              });
                        job.Store.Add(PlayingMusic);
                        job.Store.Add(ResultMusics);
                        job.JobCompleted += (s, arg) => WaitScreen.Hide();
                        job.Start();
                    }
                    if (PlayingMusic != null)
                    {
                        if (IsPlaying)
                        {
                            MusicPlayer.Stop();
                            MusicPlayer.Close();
                            MusicPlayer.Play();
                            UpdateTimer(null);
                        }
                        else
                        {
                            MusicPlayer.Pause();
                        }
                    }
                    CanPlay = SelectedMusic != null || PlayingMusic != null;
                    break;
                case "SelectedMusic":
                    CanPlay = SelectedMusic != null || PlayingMusic != null;
                    break;
                case "SelectedMusicDJ":
                    if (PlayingMusic != null)
                    {
                        SelectedMusicDJ.Initialize(PlayingMusic);
                    }
                    break;
                case "MusicPlayer":
                    MusicPlayer.MediaEnded += HandleMediaEndedEvent;
                    MusicPlayer.MediaOpened += HandleMediaOpenedEvent;
                    MusicPlayer.MediaFailed += HandleMediaFailedEvent;
                    break;
                case "NaturalDuration":
                    DatabaseManager.Instance.UpdateDuration(PlayingMusic, NaturalDuration);
                    break;
                case "Position":
                    if (IsUpdatingTime == false)
                        MusicPlayer.Position = Position;
                    break;
                case "SelectedMovie":
                    if (e.NewValue != null)
                    {
                        var movie = ((Movie)e.NewValue);
                        movie.PropertyChanged += MoviePropertyChangedHandler;
                        if (SelectedProfile!=null && movie.IsNew)
                            DatabaseManager.Instance.MarkAsRead(movie,SelectedProfile);
                    }
                    if (e.OldValue != null)
                    {
                        ((Movie)e.OldValue).PropertyChanged -= MoviePropertyChangedHandler;
                    }
                    RaiseCanExecuteChanged();
                    break;
            }
        }

        public void RefreshResult()
        {
            RegistryHelper.ShowDrivesMoviesOnly = ShowDrivesMoviesOnly;
            WaitScreen.Show();
            if (RegistryHelper.LastResultType == LibraryItemType.MusicType)
            {
                SetMusicResults(ResultMusics.ToArray());
            }
            else
            {
                if (_originalResultMovies != null)
                {
                    SetMovieResults(_originalResultMovies);
                }
                DatabaseManager.Instance.UpdateTitles(ShowDrivesMoviesOnly, SelectedProfile);
            }

            WaitScreen.Hide();
        }


        private void MoviePropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            SynchronizationContextHelper.Instance.SendInBackground(d => RaiseCanExecuteChanged(), null);
        }

        private void HandleMediaFailedEvent(object sender, ExceptionRoutedEventArgs e)
        {
            if (PlayingMusic==null)
                return;
            PlayingMusic.Status = MusicStatus.Failed;
            PlayNextMusic();
        }

        private void PlayNextMusic()
        {
            if (PlayingMusic.Status == MusicStatus.Playing)
            {
                DatabaseManager.Instance.AdvanceScore(SelectedProfile, PlayingMusic, 0.5);
            }
            if (SelectedMusicDJ.CanMoveNext)
                SelectedMusicDJ.Next();
        }

        private void HandleMediaOpenedEvent(object sender, RoutedEventArgs e)
        {
            PlayingMusic.Status = MusicStatus.Playing;
        }

        private void HandleMediaEndedEvent(object sender, RoutedEventArgs e)
        {
            if (IsPlaying == false)
                return;
            if (PlayingMusic != null)
            {
                DatabaseManager.Instance.AdvanceScore(SelectedProfile, PlayingMusic, 0.5);
            }
            if (SelectedMusicDJ.CanMoveNext)
            {
                SelectedMusicDJ.Next();
            }
        }

        private void UpdateTimer(object state)
        {
            IsUpdatingTime = true;
            Position = MusicPlayer.Position;
            NaturalDuration = MusicPlayer.NaturalDuration;
            if (NaturalDuration.HasTimeSpan)
            {
                TimeRemains = Position - NaturalDuration.TimeSpan;
            }
            IsUpdatingTime = false;
        }

        protected bool IsUpdatingTime { get; set; }


        public TimeSpan TimeRemains
        {
            get { return (TimeSpan)GetValue(TimeRemainsProperty); }
            set { SetValue(TimeRemainsProperty, value); }
        }

        public static readonly DependencyProperty TimeRemainsProperty =
            DependencyProperty.Register("TimeRemains", typeof(TimeSpan), typeof(LibraryDataSource), new UIPropertyMetadata(default(TimeSpan)));



        public Duration NaturalDuration
        {
            get { return (Duration)GetValue(NaturalDurationProperty); }
            set { SetValue(NaturalDurationProperty, value); }
        }

        public static readonly DependencyProperty NaturalDurationProperty =
            DependencyProperty.Register("NaturalDuration", typeof(Duration), typeof(LibraryDataSource), new UIPropertyMetadata(default(Duration), OnPropertyChanged));


        public TimeSpan Position
        {
            get { return (TimeSpan)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }

        public static readonly DependencyProperty PositionProperty =
            DependencyProperty.Register("Position", typeof(TimeSpan), typeof(LibraryDataSource), new UIPropertyMetadata(default(TimeSpan), OnPropertyChanged));



        private Uri GetPlayingMusicUri(string fullPath)
        {
            if (PlayingMusic != null)
                return new Uri(String.Format("file://{0}", fullPath));
            return null;
        }

        public void AddFilesInBackground(IEnumerable<string> fileNames)
        {
            var fileArray = fileNames.ToArray();
            StatusMessageService.SetStatusMessage("Adding files");
            WaitScreen.Show(fileArray.Length,1);
            DatabaseManager.Instance.TurnOffSaving = true;

            var job = new Job(ChildAddFilesAction);
            job.Store.Add(fileArray);
            job.StartBlocked();

            DatabaseManager.Instance.TurnOffSaving = false;
            DatabaseManager.Instance.UpdateNewMovieTitle(ShowDrivesMoviesOnly, SelectedProfile);
            DatabaseManager.Instance.UpdateProcessingTitle(ShowDrivesMoviesOnly);
            StatusMessageService.SetStatusMessage(StatusMessages.Ready);
            WaitScreen.Hide();

        }

        private void ChildAddFilesAction(Job obj)
        {
            var fileNames = obj.Root.Store.GetObject<string[]>();
            foreach (var fileName in fileNames)
            {
                AddFile(fileName);
                WaitScreen.AdvanceProgress();
            }
        }

        private void AddFile(string fileName)
        {
            if (Utility.IsMusicFile(fileName))
                StatusMessageService.SetStatusMessage(String.Format(StatusMessages.AddingMusic, fileName));
            else if (Utility.IsMovieFile(fileName))
            {
                StatusMessageService.SetStatusMessage(String.Format(StatusMessages.AddingMovie, fileName));
            }
            DatabaseManager.Instance.AddFile(fileName);
        }

        [Import]
        public StatusMessageBarPresenter StatusMessageBarPresenter { get; set; }

        public void SelectLastImportedPlaylist()
        {
            var lastImportedPlaylist = DatabaseManager.Instance.GetLastImportedPlaylist();
            SelectedLibraryItem = null;
            SelectedLibraryItem = lastImportedPlaylist;
        }

        public void SetMusicResults(IEnumerable<Music> musics)
        {
            LibraryManager.Value.ChangeViewCommand.Execute(LibraryItemType.MusicType);
            ResultMusics.Clear();
            var index = 1;
            var resultCount = musics.Count();
            WaitScreen.Show(resultCount, index - 1);
            DatabaseManager.Instance.TurnOffSaving = true;
            lock (DatabaseManager.LockObject)
            {
                foreach (var music in musics)
                {
                    music.Index = index++;
                    music.Preferance = string.Empty;

                    DatabaseManager.Instance.SetMusicStatus(music, PlayingMusic);
                    DatabaseManager.Instance.AdvanceScore(SelectedProfile, music, 0);
                    music.Preferance = string.Format("{0:0.00}", DatabaseManager.Instance.GetScore(music, SelectedProfile));
                    ResultMusics.Add(music);

                    WaitScreen.AdvanceProgress();
                }    
            }
            DatabaseManager.Instance.TurnOffSaving = false;
        }

        public void SetMovieResults(IEnumerable<Movie> movies)
        {
            SetMovieResults(movies, m => m.WeightedRating);
        }

        [Import]
        private WaitScreenService WaitScreen { get; set; }

        private void SetMovieResults(IEnumerable<Movie> movies, Func<Movie, decimal?> orderBySelector)
        {
            LibraryManager.Value.ChangeViewCommand.Execute(LibraryItemType.MovieType);
            if (movies == null)
                return;
            foreach (var movie in movies.Where(movie => movie.WeightedRating.HasValue == false))
            {
                movie.CalculateWeightedRating();
            }

            _originalResultMovies = movies;
            var resultMovies = new ObservableCollection<Movie>();
            resultMovies.Clear();
            var index = 1;
            var resultArray = ShowDrivesMoviesOnly ? movies.Where(m => m.IsLocal) : movies;
            
            var orderedArray = orderBySelector == null ? resultArray.ToArray() : resultArray.OrderByDescending(orderBySelector).ToArray();
            WaitScreen.Show(orderedArray.Length, index - 1);
            foreach (var movie in orderedArray)
            {
                movie.Index = index++;
                if ((movie.Size == null || movie.Size == 0) && File.Exists(movie.FullPath))
                {
                    DatabaseManager.Instance.UpdateMovieFileSize(movie);
                }
                resultMovies.Add(movie);

                WaitScreen.Show(orderedArray.Length, index - 1);
                Application.Current.DoEvents();
            }
            ResultMovies = resultMovies;
            SelectedMovie = ResultMovies.FirstOrDefault();
        }

        public void InitializeMovieForList(Movie movie)
        {
            var profileMovie = DatabaseManager.Instance.GetProfileMovie(SelectedProfile, movie);
            if (profileMovie == null)
            {
                if (String.IsNullOrWhiteSpace(movie.FullPath) == false)
                    movie.IsNew = true;
            }
            else
            {
                movie.IsNew = profileMovie.IsNewMovie();
                movie.IsStarred = profileMovie.Star > 0;
                movie.Watched = profileMovie.Watched;
                movie.IsInWishList = profileMovie.IsInWishList;
            }
        }

        //[Import]
        //private Lazy<MusicDurationUpdater> MusicDurationUpdater { get; set; }
        public void UpdateDuration()
        {
            var musics = ResultMusics.Where(m => String.IsNullOrWhiteSpace(m.TimeText) && m.Status != "Playing").ToArray();
            if (musics.Count() <= 0) return;
            //MusicDurationUpdater.Value.AddRange(musics);
        }

        [Import]
        private Lazy<BackgroundScannerManager> BackgroundScannerManager { get; set; }
        private void LoadSelectedItemResults()
        {
            if (SelectedLibraryItem == null)
                return;
            WaitScreen.Show();
            BackgroundScannerManager.Value.Pause();
            switch (SelectedLibraryItem.Type)
            {
                case LibraryItemType.ProcessingLibrary:
                    SetMovieResults(DatabaseManager.Instance.UpdateProcessingMovies());
                    break;
                case LibraryItemType.FailedLibrary:
                    SetMovieResults(DatabaseManager.Instance.GetFailedMovies());
                    break;
                case LibraryItemType.UnreadMovieLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetNewMovies(SelectedProfile));
                    }
                    break;
                case LibraryItemType.WatchListLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetWatchedMovies(SelectedProfile));
                    }
                    break;
                case LibraryItemType.WishListLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetWishListMovie(SelectedProfile));
                    }
                    break;
                case LibraryItemType.StaredMovieLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetStarMovies(SelectedProfile));
                    }
                    break;
                case LibraryItemType.RecommendedMovieLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetRecommendedMovies(SelectedProfile), null);
                    }
                    break;
                case LibraryItemType.RecentlyPlayedMovieLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetRecentlyPlayedMovies(SelectedProfile), null);
                    }
                    break;
                case LibraryItemType.RecentlyAddedMovieLibrary:
                    if (SelectedProfile != null)
                    {
                        SetMovieResults(DatabaseManager.Instance.GetRecentlyAddedMovies(SelectedProfile), null);
                    }
                    break;
                case LibraryItemType.IMDbMustWatchList:
                    DatabaseManager.Instance.UpdateIMDBMustWatchList();
                    ShowSelectedLibraryItemsResult();
                    break;
                case LibraryItemType.MovieLibrary:
                    SetMovieResults(DatabaseManager.Instance.GetAllMovies());
                    break;
                case LibraryItemType.MusicLibrary:
                    SetMusicResults(DatabaseManager.Instance.GetAllMusics());
                    UpdateDuration();
                    break;
                
                default:
                    ShowSelectedLibraryItemsResult();
                    break;
            }

            BackgroundScannerManager.Value.Resume();
            EventAggregator.GetEvent<BaseEvent<PerformSelectedItemActionEventArgs>>().Publish(new PerformSelectedItemActionEventArgs(this, SelectedLibraryItem));
            WaitScreen.Hide();
        }

        private void ShowSelectedLibraryItemsResult()
        {
            var job = new Job(ExecuteGetAllMoviesOrMusics);
            job.Store.Add(SelectedLibraryItem);
            job.StartBlocked();
            if (LibraryItemType.IsMusicItem(SelectedLibraryItem.Type))
            {
                SetMusicResults(job.Store.GetObject<IEnumerable<Music>>());
                UpdateDuration();
            }
            else
            {
                SetMovieResults(job.Store.GetObject<IEnumerable<Movie>>());
            }
        }

        private static void ExecuteGetAllMoviesOrMusics(Job obj)
        {
            var libraryItem = obj.Store.GetObject<LibraryItem>();
            if (LibraryItemType.IsMusicItem(libraryItem.Type))
            {
                obj.Store.Add(libraryItem.GetAllMusic());
            }
            else
            {
                obj.Store.Add(libraryItem.GetAllMovies());
            }
        }

        public void Play(Music music)
        {
            if (PlayingMusic == music)
                return;
            PlayingMusic = music;
            DatabaseManager.Instance.AdvanceScore(SelectedProfile, PlayingMusic, 0.5);
            IsPlaying = true;
        }


        public MediaElement MusicPlayer
        {
            get { return (MediaElement)GetValue(MusicPlayerProperty); }
            set { SetValue(MusicPlayerProperty, value); }
        }

        public static readonly DependencyProperty MusicPlayerProperty =
            DependencyProperty.Register("MusicPlayer", typeof(MediaElement), typeof(LibraryDataSource), new UIPropertyMetadata(null, OnPropertyChanged));


        public bool IsPlaying
        {
            get { return (bool)GetValue(IsPlayingProperty); }
            set { SetValue(IsPlayingProperty, value); }
        }

        public Music SelectedMusic
        {
            get { return (Music)GetValue(SelectedMusicProperty); }
            set { SetValue(SelectedMusicProperty, value); }
        }

        public static readonly DependencyProperty SelectedMusicProperty =
            DependencyProperty.Register("SelectedMusic", typeof(Music), typeof(LibraryDataSource), new UIPropertyMetadata(null, OnPropertyChanged));



        public static readonly DependencyProperty IsPlayingProperty =
            DependencyProperty.Register("IsPlaying", typeof(bool), typeof(LibraryDataSource), new UIPropertyMetadata(false, OnPropertyChanged));


        public void AddFolderInBackground(string folderPath)
        {
            AddFilesInBackground(CollectFileNames(folderPath));
        }

        [Import]
        private StatusMessageService StatusMessageService { get; set; }

        private IEnumerable<string> CollectFileNames(string folderPath)
        {
            StatusMessageService.SetStatusMessage("Searching folder for musics and movies");
            var files = new List<string>();
            new Job(j => AddFolderRecursively(folderPath, files)).StartBlocked();
            return files;
        }

        private static void AddFolderRecursively(string folderPath, List<string> files)
        {
            files.AddRange(Directory.GetFiles(folderPath, "*.mp3"));
            files.AddRange(Directory.GetFiles(folderPath, "*.wma"));
            foreach (var extension in Utility.SupportedMovieFileExtension)
            {
                files.AddRange(Directory.GetFiles(folderPath, "*" + extension));
            }

            foreach (string directory in Directory.GetDirectories(folderPath))
            {
                AddFolderRecursively(directory, files);
            }
        }

        public void SelectMusicDJ(string dJName)
        {
            SelectedMusicDJ = MusicDjs.FirstOrDefault(d => d.Name == dJName);
        }

        public void StartMusicDJ(Music playingMusic)
        {
            if (playingMusic == null || SelectedMusicDJ == null) return;
            SelectedMusicDJ.Initialize(playingMusic);
        }

        public void SelectProfile(string profileName)
        {
            SelectedProfile = DatabaseManager.Instance.GetProfile(profileName);
        }

        public Profile SelectedProfile
        {
            get { return _selectedProfile; }
            set
            {
                _selectedProfile = value;
                OnPropertyChanged("SelectedProfile");
            }
        }


        public Movie SelectedMovie
        {
            get { return (Movie)GetValue(SelectedMovieProperty); }
            set { SetValue(SelectedMovieProperty, value); }
        }

        public static readonly DependencyProperty SelectedMovieProperty =
            DependencyProperty.Register("SelectedMovie", typeof(Movie), typeof(LibraryDataSource), new UIPropertyMetadata(null, OnPropertyChanged));

        private IEnumerable<Movie> _originalResultMovies;
        private Profile _selectedProfile;
        public TimeSpan OldPosition { get; private set; }

        public bool IsPlaylistExists(string playlistTitle)
        {
            return Playlists.Any(p => String.Compare(p.Title, playlistTitle, StringComparison.OrdinalIgnoreCase) == 0);
        }

        public LibraryItem CreatePlaylist(string playlistTitle)
        {
            var playlist = DatabaseManager.Instance.CreatePlaylist(playlistTitle);
            Playlists.Add(playlist);
            return playlist;
        }

        public LibraryItem GetPlaylist(string playlistTitle)
        {
            return Playlists.FirstOrDefault(p => String.Compare(p.Title, playlistTitle) == 0);
        }

        public void ResumeClosedMusic()
        {
            if (PlayingMusic != null)
            {
                MusicPlayer.Source = GetPlayingMusicUri(PlayingMusic.FullPath);
                MusicPlayer.Position = OldPosition;
                if (IsPlaying)
                {
                    MusicPlayer.Play();
                }
            }
        }

        public void PauseAndClose()
        {
            OldPosition = Position;
            MusicPlayer.Stop();
            MusicPlayer.Close();
        }


        public void UpdateMovie(Movie movie, JObject jsonObject)
        {
            var newMovie = DatabaseManager.Instance.UpdateMovie(movie, jsonObject);
            if (movie.Equals(newMovie) == false)
            {
                var resultMovie = ResultMovies.FirstOrDefault(m => m.Equals(movie));
                if (resultMovie != null)
                {
                    newMovie.Index = resultMovie.Index;
                    ResultMovies.Remove(movie);
                    InsertMovieToCurrentResult(newMovie);
                    var originalResult = _originalResultMovies.ToList();
                    originalResult.Remove(resultMovie);
                    _originalResultMovies = originalResult;
                }
            }
        }

        private void InsertMovieToCurrentResult(Movie newMovie)
        {
            ResultMovies.Insert(newMovie.Index - 1, newMovie);
        }

        public void SelectYear(long year)
        {
            SelectedLibraryItem = DatabaseManager.Instance.GetYearLibraryItem(year);
        }

        private void Play(Movie movie, string fileToPlay)
        {
            DatabaseManager.Instance.IncrementPlayCount(movie, SelectedProfile);
            SystemHelper.PlayMovie(fileToPlay);
        }

        private static bool VerifyFileLocation(Movie movie, ref string fileToPlay)
        {
            var isExist = true;
            if (string.IsNullOrEmpty(fileToPlay) || File.Exists(fileToPlay) == false)
            {
                if ( MessageBox.Show(String.Format("Movie {0} not found. Press Ok to browse the movie?", movie.Title), "File not found", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                {
                    var fileDialog = new OpenFileDialog { Filter = Utility.SupportedMovieFileFilter, Multiselect = false };
                    if (fileDialog.ShowDialog() == true)
                    {
                        DatabaseManager.Instance.UpdateMovieLocation(movie, fileDialog.FileName);
                        fileToPlay = fileDialog.FileName;
                    }
                    else
                    {
                        isExist = false;
                    }
                }
                else
                {
                    isExist = false;
                }
            }
            return isExist;
        }

        public void InitializeDatabase()
        {
            MusicPlayer.Volume = RegistryHelper.Volume;
            MusicPlayer.IsMuted = RegistryHelper.IsMuted;
            if (MediaAssistantSqliteDbEntities.IsUpdatedVersion == false)
            {
                Logger.Instance.Write("Executing ConvertOldVersionMusicLibrary");
                DatabaseManager.Instance.ConvertOldVersionMusicLibrary();
            }
            if(string.IsNullOrEmpty(RegistryHelper.ProfileName)==false)
                SelectProfile(RegistryHelper.ProfileName);
            LoadLibrary();
            var selectedMusicDj = MusicDjs.SingleOrDefault(m => m.Name == RegistryHelper.SelectedMusicDJ);
            if (selectedMusicDj != null)
                SelectedMusicDJ = selectedMusicDj;
        }

        public void Remove(IEnumerable<Music> musics)
        {
            DatabaseManager.Instance.Remove(musics);
            foreach (var music in musics)
            {
                SynchronizationContextHelper.Instance.Send(m=>ResultMusics.Remove((Music) m),music);
            }
        }
    }
}